#include "fs.h"

#if ENABLE_SEMAPHORE

#include <semaphore.h>
#include "param.h"
#include "fproc.h"
#include <minix/callnr.h>
#define ALREADY_ASSIGNED  -100
/*===============================================*
 *		        do_init		                     *
 *===============================================*/
PUBLIC int do_init(){
	int i, j;
	/* Buscamos un semaforo no inicializado */
	for(i=MAX_NAMED_SEM;i<MAX_SEM;i++){
		if(smtable[i].sm_valid == FALSE){
			reply_i1 = i; /* Vamos a devolver el id que inicializamos */
			/* Hacemos la inicializacion propiamente dicha */
                        
			smtable[i].sm_valid = TRUE;
			smtable[i].sm_value = sem_value;
			smtable[i].sm_first = -1;
			smtable[i].sm_last = -1;
			smtable[i].sm_cant=1;
			/* Inicializamos la cola de procesos y la tabla de procesos asignados */
			for(j=0;j<NR_PROCS;j++){
				smtable[i].sm_procs[j].sm_next = -1;
                smtable[i].sm_procs[j].sm_prev = -1;
                smtable[i].sm_procs[j].sm_use = 0;
                smtable[i].sm_procs[j].sm_pid = 0;
                smtable[i].sm_assigned[j]=0;
			}
			/* Lo asignamos al llamador */
			smtable[i].sm_assigned[who]=1;
			return 0;
		}
	}
	/* No hay semaforos vacios */
    reply_i1 = -1;
	return -1;
}

/*===============================================*
 *		         do_assign		                 *
 *===============================================*/
PUBLIC int do_assign(){
	int i, j,id;
	struct smtable* sm;
        id = sem_id; 
        /*  Chequeo validez del Id  */
	if (id < 0) return -1; /* Chequeo que sea un numero positivo */
	if (id >= MAX_NAMED_SEM) return -1; /*Chequeo que este en el rango corespondiente definido con anterioridad */    
        sm = &smtable[id];
	/* Si no esta inicializado, hay que hacerlo */
       if (sm->sm_valid == 0){
		i=id;
		reply_i1 = i; /* En la respuesta colocamos el numero del id, que es el numero que se nos pidio */
		smtable[i].sm_valid = TRUE;
		smtable[i].sm_value = sem_value;
		smtable[i].sm_first = -1;
		smtable[i].sm_last = -1;
	
		for(j=0;j<NR_PROCS;j++){
	      smtable[i].sm_procs[j].sm_next = -1;
	      smtable[i].sm_procs[j].sm_prev = -1;
	      smtable[i].sm_procs[j].sm_use = 0;
	      smtable[i].sm_procs[j].sm_pid = 0;
	      smtable[i].sm_assigned[j]=0;
        }
        smtable[i].sm_assigned[who]=1;
        smtable[i].sm_cant=1;

		return 0;
		
    }
    else{ /*Si ya estaba, solo agregamos al proceso, se ignora el valor pasado por este */
        if(smtable[id].sm_assigned[who] == 1){
           reply_i1 = id;
           return ALREADY_ASSIGNED;
        }
        smtable[id].sm_assigned[who]=1;
        smtable[id].sm_cant+=1;
        reply_i1 = id;
        return 0;
   }
}

/*===============================================*
 *		          do_wait                        *
 *===============================================*/
PUBLIC int do_wait(){
    int id;
    struct smtable* sm;
    id = sem_id;
    
    /*  Chequeo validez del Id  */
    if (id < 0) return -1; /* Chequeo que sea un numero positivo */
    if (id >= MAX_SEM) return -1; /*Chequeo que esste en el rango corespondiente definido con anterioridad */
    
    sm = &smtable[id];
    
    /* Chequeo que sea un semaforo valido */	
    if (sm->sm_valid == 0) return -1;
	/* Chequeo que este asignado al proceso */

    if (!(sm->sm_assigned[who])) return -1;	

    if(sm->sm_value > 0){
		/* Si es mayor a 0 el semaforo continuamos sin problemas */
		sm->sm_value--;
	}
	else{
		/* Si es 0 el semaforo no respondemos, asi paramos la ejecucion */		
		dont_reply = TRUE;
		/* Lo marcamos como suspendido y ponemos como causa XSEM, es decir suspendido por semaforo */
		fp->fp_suspended = SUSPENDED;
		fp->fp_task = -XSEM;

        /* Guardamos la informacion necesaria para volver a despertar el proceso */
	        fp->fp_fd = fd << 8 | fs_call;
		
		/* y nos registramos en la lista de espera para este semaforo */
		if(sm->sm_first == -1){
            /* si no hay ningun proceso inicializamos la cola */
			sm->sm_first = who;
            sm->sm_procs[who].sm_prev = -1;
		}
		else{
			sm->sm_procs[sm->sm_last].sm_next = who;
		    sm->sm_procs[who].sm_prev = sm->sm_last; 
        }
		
		sm->sm_procs[who].sm_next = -1;
        sm->sm_procs[who].sm_use = 1;
        sm->sm_procs[who].sm_pid = fp->fp_pid;
		sm->sm_last = who;
	        
    }
	
	return 0;
}


/*===============================================*
 *		            do_post		                 *
 *===============================================*/
PUBLIC int do_post(){
    int proc, id;
    pid_t sm_pid;
    struct smtable* sm;
    register struct fproc *rp;
    id = sem_id;
    
    /*  Chequeo validez del Id  */
    if (id < 0) return -1; /* Chequeo que sea un numero positivo */
    if (id >= MAX_SEM) return -1; /* Chequeo que esste en el rango corespondiente definido con anterioridad */
    
    sm = &smtable[id];
    
    /* Chequeo que sea un semaforo valido */	
    if (sm->sm_valid == 0) return -1; 
	/* y asignado */
    if (!(sm->sm_assigned[who])) return -1;
	
	/* Revisamos si hay procesos en espera para este semaforo */
	while(sm->sm_first != -1){
	
		/* Estamos usado FIFO asi q revivimos el primero en la lista */
		proc = sm->sm_first;
		
		/* Lo eliminamos de la lista */		
		sm->sm_first = sm->sm_procs[proc].sm_next;
        sm_pid = sm->sm_procs[proc].sm_pid;
		sm->sm_procs[proc].sm_next  = -1;
		sm->sm_procs[proc].sm_prev  = -1; /* invalidamos al proceso que sale del semaforo */
		sm->sm_procs[proc].sm_use  = 0;
        
		/*Ahora actualizamos la cola */
        if(sm->sm_first != -1){
            sm->sm_procs[sm->sm_first].sm_prev  = -1; /*si el que asigne como primero es valido coloco su puntero*/
                                                      /* anterior a -1 */
        }
        if(sm->sm_first == -1){
			sm->sm_last = -1;
		}
		
		rp = &fproc[proc];
		
		/* Informamos al sistema q tenemos q revivir el proceso */
        /* Por seguridad se realizan chequeos para asegurarnos que el proceso esta suspendido por el semaforo */
		if(rp->fp_pid == sm_pid && (rp->fp_fd & BYTE) == SEM_WAIT && rp->fp_suspended == SUSPENDED ){
			/* Lo marcamos y respondemos para despertarlo */
			rp->fp_suspended = NOT_SUSPENDED;
			reply(proc,0);
			return 0;
		}
	}
	
	/* Incrementamos el valor del semaforo de 1 */
	sm->sm_value++;
	
	return 0;
}



/*===============================================*
 *		           do_getvalue	        	     *
 *===============================================*/
PUBLIC int do_getvalue(){
    int id;
    struct smtable* sm;
    id = sem_id;
    
    /*  Chequeo validez del Id  */
    if (id < 0) return -1; /* Chequeo que sea un numero positivo */
    if (id >= MAX_SEM) return -1; /*Chequeo que esste en el rango corespondiente definido con anterioridad */
    
    sm = &smtable[id];
    
    /* Chequeo que sea un semaforo valido */	
    if (sm->sm_valid == 0) return -1;
	/* y asignacion */
    if (!(sm->sm_assigned[who])) return -1;
	/* Devolvemos el valor del semaforo */
	reply_i1 = sm->sm_value;
	
	return 0;
}

/*===============================================*
 *		           do_destroy		 *
 *===============================================*/
PUBLIC int do_destroy(){
    int id, proc;
    struct smtable* sm;
    register struct fproc *rp;
    pid_t sm_pid;
    id = sem_id;
    
    /*  Chequeo validez del Id  */
    if (id < 0) return -1; /* Chequeo que sea un numero positivo */
    if (id >= MAX_SEM) return -1; /*Chequeo que esste en el rango corespondiente definido con anterioridad */
    
    sm = &smtable[id];
    
    /* Chequeo que sea un semaforo valido */	
    if (sm->sm_valid == 0) return -1;
    if (!(sm->sm_assigned[who])) return -1;

    proc = sm->sm_first;
    sm_pid = sm->sm_procs[proc].sm_pid;
    rp = &fproc[proc];
	
    /* Si hay procesos en espera los despertamos a todos */
	while(proc != -1){
        /* Despertamos a los procesos que tenemos dormidos, nuevamente hay chequeos por las dudas */
        if(sm_pid == rp->fp_pid && (rp->fp_fd & BYTE) == SEM_WAIT && rp->fp_suspended == SUSPENDED){
	   rp->fp_suspended = NOT_SUSPENDED;
           reply(proc,0);
        }
	proc = sm->sm_procs[proc].sm_next;
        sm_pid = sm->sm_procs[proc].sm_pid;
        rp = &fproc[proc];
	}
	
	/* Invalidamos el semaforo */
	sm->sm_valid = 0;
        sm->sm_cant=0;
	
	return 0;
}

/*===============================================*
 *		         do_getqueuesize	     		 *
 *===============================================*/
PUBLIC int do_getqueuesize(){
    int size, id, proc; 
    struct smtable* sm;
    id = sem_id;
    
    /*  Chequeo validez del Id  */
    if (id < 0) return -1; /* Chequeo que sea un numero positivo */
    if (id >= MAX_SEM) return -1; /*Chequeo que esste en el rango corespondiente definido con anterioridad */
    
    sm = &smtable[id];
    
    /* Chequeo que sea un semaforo valido */	
    if (sm->sm_valid == 0) return -1;
	/* y asignacion */
    if (!(sm->sm_assigned[who])) return -1;

    proc = sm->sm_first;
    size = 0;
	
	/* Recorremos la cola */
	while(proc != -1){
		size++;
		proc = sm->sm_procs[proc].sm_next;
	}
	
	reply_i1 = size;
	
	return 0;
}



/*===============================================*
 *		            do_freesem			         *
 *===============================================*/
PUBLIC int do_freesem(){
	int i, j,id;
	struct smtable* sm;
    id = sem_id; 
    /*  Chequeo validez del Id  */
	if (id < 0) return -1; /* Chequeo que sea un numero positivo */
	if (id >= MAX_SEM) return -1; /*Chequeo que esste en el rango corespondiente definido con anterioridad */    
    sm = &smtable[id];
    if (sm->sm_valid == 0) return -1; /* si no esta inicializado no se puede liberar */
    if (!(sm->sm_assigned[who])) return -1; /* Si no esta asignado, no se puede liberar */
    sm->sm_assigned[who] = 0; /* se saca al proceso de la tabla */
    sm->sm_cant -=1; /* ahora hay un proceso menos asignado */
    if(id >= MAX_NAMED_SEM && (sm->sm_cant == 0)){ /* Si era sin nombre, y nadie lo tiene asignado, se libera */
      sm->sm_valid = 0;
    }
    return 0;
}

/*===============================================*
 *		             do_issem			         *
 *===============================================*/
PUBLIC int do_issem(){
	int i, j,id;
	struct smtable* sm;
    id = sem_id; 
    /*  Chequeo validez del Id  */
	if (id < 0) return -1; /* Chequeo que sea un numero positivo */
	if (id >= MAX_SEM) return -1; /*Chequeo que este en el rango corespondiente definido con anterioridad */    
    sm = &smtable[id];
    if (sm->sm_valid == 0) return 0; /* No inicializado, en particular no asignado :D */
    if (sm->sm_assigned[who]) return 1; /* es asignado */
    else return 0; /* no estaba asignado */
}

#endif
